<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>3D Snake Game - Enhanced</title> <style> body { margin: 0; overflow: hidden; background-color: #111; font-family: 'Arial', sans-serif; touch-action: none; } canvas { display: block; } #score { position: absolute; top: 20px; left: 20px; color: white; font-size: 24px; text-shadow: 0 0 5px #0ff; z-index: 100; } #game-over { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-size: 48px; text-align: center; display: none; text-shadow: 0 0 10px #f00; z-index: 100; } #restart { margin-top: 20px; padding: 12px 24px; font-size: 20px; background: linear-gradient(45deg, #ff8a00, #e52e71); color: white; border: none; border-radius: 25px; cursor: pointer; box-shadow: 0 0 15px rgba(229, 46, 113, 0.7); transition: all 0.3s ease; } #restart:hover { transform: scale(1.05); box-shadow: 0 0 20px rgba(229, 46, 113, 0.9); } #controls { position: absolute; bottom: 20px; left: 20px; color: rgba(255, 255, 255, 0.7); font-size: 14px; z-index: 100; } </style> </head> <body> <div id="score">Score: 0</div> <div id="game-over"> Game Over!<br> <button id="restart">Play Again</button> </div> <div id="controls"> Arrow Keys: X/Y movement | W/S: Z movement | Drag to rotate view </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script> // Enhanced 3D Snake Game with fixed controls let score = 0; let gameOver = false; let snake = []; let food = null; let direction = { x: 1, y: 0, z: 0 }; let nextDirection = { x: 1, y: 0, z: 0 }; let lastUpdateTime = 0; let gameSpeed = 150; let speedIncreaseInterval = null; // Scene setup with better visuals const scene = new THREE.Scene(); scene.background = new THREE.Color(0x111122); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; document.body.appendChild(renderer.domElement); // Enhanced lighting const ambientLight = new THREE.AmbientLight(0x404040, 0.5); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(1, 1, 1); directionalLight.castShadow = true; scene.add(directionalLight); const hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 0.5); scene.add(hemisphereLight); // Camera position camera.position.set(25, 25, 25); camera.lookAt(0, 0, 0); // Mouse controls for camera let isDragging = false; let previousMousePosition = { x: 0, y: 0 }; document.addEventListener('mousedown', (e) => { isDragging = true; previousMousePosition = { x: e.clientX, y: e.clientY }; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const deltaMove = { x: e.clientX - previousMousePosition.x, y: e.clientY - previousMousePosition.y }; // Rotate camera around the scene camera.position.x -= deltaMove.x * 0.1; camera.position.y += deltaMove.y * 0.1; camera.lookAt(0, 0, 0); previousMousePosition = { x: e.clientX, y: e.clientY }; }); document.addEventListener('mouseup', () => { isDragging = false; }); // Touch controls for mobile document.addEventListener('touchstart', (e) => { isDragging = true; previousMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY }; e.preventDefault(); }); document.addEventListener('touchmove', (e) => { if (!isDragging) return; const deltaMove = { x: e.touches[0].clientX - previousMousePosition.x, y: e.touches[0].clientY - previousMousePosition.y }; camera.position.x -= deltaMove.x * 0.1; camera.position.y += deltaMove.y * 0.1; camera.lookAt(0, 0, 0); previousMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY }; e.preventDefault(); }); document.addEventListener('touchend', () => { isDragging = false; }); // Larger game grid const gridSize = 30; // Create grid helper for better orientation const gridHelper = new THREE.GridHelper(gridSize, gridSize, 0x555555, 0x333333); scene.add(gridHelper); // Create snake segment with better visuals function createSnakeSegment(x, y, z, isHead = false) { const geometry = new THREE.BoxGeometry(0.9, 0.9, 0.9); const material = new THREE.MeshPhongMaterial({ color: isHead ? 0x00ffaa : 0x00cc88, emissive: isHead ? 0x00aa77 : 0x008855, emissiveIntensity: 0.2, shininess: 30 }); const cube = new THREE.Mesh(geometry, material); cube.position.set(x, y, z); cube.castShadow = true; cube.receiveShadow = true; scene.add(cube); return cube; } // Initialize snake with more segments function initSnake() { snake = []; for (let i = 0; i < 5; i++) { snake.push(createSnakeSegment(-i, 0, 0, i === 0)); } } initSnake(); // Create food with better visuals function createFood() { const x = Math.floor(Math.random() * (gridSize-2)) - Math.floor(gridSize / 2) + 1; const y = Math.floor(Math.random() * (gridSize-2)) - Math.floor(gridSize / 2) + 1; const z = Math.floor(Math.random() * (gridSize-2)) - Math.floor(gridSize / 2) + 1; const geometry = new THREE.SphereGeometry(0.5, 32, 32); const material = new THREE.MeshPhongMaterial({ color: 0xff5555, emissive: 0xff0000, emissiveIntensity: 0.3 }); const sphere = new THREE.Mesh(geometry, material); sphere.position.set(x, y, z); sphere.castShadow = true; scene.add(sphere); // Add pulsing animation let scale = 1; let pulseDirection = 0.02; function animateFood() { scale += pulseDirection; if (scale > 1.2 || scale < 0.8) { pulseDirection = -pulseDirection; } sphere.scale.set(scale, scale, scale); if (!gameOver) requestAnimationFrame(animateFood); } animateFood(); return sphere; } // First food food = createFood(); // Handle keyboard input (fixed controls) const keyState = {}; document.addEventListener('keydown', (event) => { keyState[event.key] = true; // Prevent arrow keys from scrolling the page if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'w', 'W', 's', 'S'].includes(event.key)) { event.preventDefault(); } }); document.addEventListener('keyup', (event) => { keyState[event.key] = false; }); function updateDirection() { // Only change direction if not moving in the opposite direction if (keyState['ArrowUp'] && direction.y === 0 && direction.z === 0) { nextDirection = { x: 0, y: 1, z: 0 }; } if (keyState['ArrowDown'] && direction.y === 0 && direction.z === 0) { nextDirection = { x: 0, y: -1, z: 0 }; } if (keyState['ArrowLeft'] && direction.x === 0 && direction.z === 0) { nextDirection = { x: -1, y: 0, z: 0 }; } if (keyState['ArrowRight'] && direction.x === 0 && direction.z === 0) { nextDirection = { x: 1, y: 0, z: 0 }; } if ((keyState['w'] || keyState['W']) && direction.z === 0 && direction.y === 0) { nextDirection = { x: 0, y: 0, z: 1 }; } if ((keyState['s'] || keyState['S']) && direction.z === 0 && direction.y === 0) { nextDirection = { x: 0, y: 0, z: -1 }; } } // Check collision with walls or self function checkCollision(x, y, z) { // Check walls (smaller boundary than grid for better playability) const boundary = gridSize/2 - 1; if (Math.abs(x) > boundary || Math.abs(y) > boundary || Math.abs(z) > boundary) { return true; } // Check self (skip head) for (let i = 1; i < snake.length; i++) { if (Math.abs(snake[i].position.x - x) < 0.9 && Math.abs(snake[i].position.y - y) < 0.9 && Math.abs(snake[i].position.z - z) < 0.9) { return true; } } return false; } // Game loop function animate(currentTime) { requestAnimationFrame(animate); // Update direction based on key state updateDirection(); // Game update if (!gameOver && (!lastUpdateTime || currentTime - lastUpdateTime > gameSpeed)) { lastUpdateTime = currentTime; direction = { ...nextDirection }; const head = snake[0]; const newX = head.position.x + direction.x; const newY = head.position.y + direction.y; const newZ = head.position.z + direction.z; if (checkCollision(newX, newY, newZ)) { gameOver = true; document.getElementById('game-over').style.display = 'block'; return; } const newHead = createSnakeSegment(newX, newY, newZ, true); snake.unshift(newHead); // Make previous head no longer the head if (snake.length > 1) { snake[1].material.color.setHex(0x00cc88); snake[1].material.emissive.setHex(0x008855); } if (Math.abs(newX - food.position.x) < 1 && Math.abs(newY - food.position.y) < 1 && Math.abs(newZ - food.position.z) < 1) { score++; document.getElementById('score').textContent = `Score: ${score}`; scene.remove(food); food = createFood(); // Increase speed every 5 points if (score % 5 === 0) { clearTimeout(speedIncreaseInterval); gameSpeed = Math.max(50, gameSpeed - 10); } } else { const tail = snake.pop(); scene.remove(tail); } } renderer.render(scene, camera); } // Handle window resize window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // Restart game (fixed) document.getElementById('restart').addEventListener('click', () => { // Clear snake and food snake.forEach(segment => scene.remove(segment)); if (food) scene.remove(food); // Reset game state score = 0; gameOver = false; direction = { x: 1, y: 0, z: 0 }; nextDirection = { x: 1, y: 0, z: 0 }; lastUpdateTime = 0; gameSpeed = 150; // Recreate snake and food initSnake(); food = createFood(); // Reset camera position camera.position.set(25, 25, 25); camera.lookAt(0, 0, 0); // Update UI document.getElementById('score').textContent = `Score: ${score}`; document.getElementById('game-over').style.display = 'none'; }); // Start animation animate(); </script> </body> </html>
Memory